package org.ycb.ycbisgod.ycbwoxihuanni.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.ycb.ycbisgod.ycbwoxihuanni.entity.Node;
import org.ycb.ycbisgod.ycbwoxihuanni.entity.NodeNextNode;
import org.ycb.ycbisgod.ycbwoxihuanni.mapper.NodeMapper;
import org.ycb.ycbisgod.ycbwoxihuanni.mapper.NodeNextNodeMapper;
import org.ycb.ycbisgod.ycbwoxihuanni.service.INodeService;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements INodeService {
    @Autowired
    private NodeNextNodeMapper nodeNextNodeMapper;


    @Override
    public boolean save(Node entity) {
        return super.save(entity);
    }

    /*
     *根据主人id获取节点
     */
    @Override
    public Node findByOwnerId(Integer OwnerId) {
        return baseMapper.findByOwnerId(OwnerId);
    }

    /*
     *根据节点id获取子节点
     */
    @Override
    public List<Node> getChildren(Integer nodeId) {
        return baseMapper.findChildrenByNodeId(nodeId);
    }

    /*
     *根据节点id获取父节点
     */
    @Override
    public List<Node> getParents(Integer nodeId) {
        // 调用 NodeMapper 中自定义的 findParentsByNodeId 方法，获取指定节点的所有父节点
        return baseMapper.findParentsByNodeId(nodeId);
    }


    @Override
    public List<Node> list() {
        return super.list();
    }

    /**
     * 添加一个节点的子节点关系。
     * 这个方法用于**连接两个已存在的节点**。
     * 在添加前会检查是否存在父子关系，并保证不出现环状结构。
     *
     * @param nodeId 父节点的ID
     * @param nextnodeId 子节点的ID
     * @return 是否添加成功
     */
    @Override
    @Transactional
    public boolean addNextNodeConnect(Integer nodeId, Integer nextnodeId) {
        // 1. 检查父节点和子节点是否存在
        if (baseMapper.selectById(nodeId) == null || baseMapper.selectById(nextnodeId) == null) {
            // 节点不存在，无法建立关系
            return false;
        }
        // 2. 检查关系是否已存在
        QueryWrapper<NodeNextNode> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("node_id", nodeId).eq("nextnode_id", nextnodeId);
        Long count = nodeNextNodeMapper.selectCount(queryWrapper);
        if (count != null && count > 0) {
            // 关系已存在
            return false;
        }
        // 3. 检查是否会形成环状结构 (从子节点nextnodeId开始，能否到达父节点nodeId)
        if (hasPath(nextnodeId, nodeId)) {
            // 形成环状结构，不允许添加
            // 可以在此处抛出自定义异常或返回特定的错误码，以便控制器返回更具体的消息
            // throw new RuntimeException(MessageConstant.NODE_CYCLE_DETECTED);
            return false;
        }
        // 4. 添加关系
        NodeNextNode nodeNextNode = new NodeNextNode();
        nodeNextNode.setNodeId(nodeId);
        nodeNextNode.setNextnodeId(nextnodeId);

        int result = nodeNextNodeMapper.insert(nodeNextNode);
        return result > 0;
    }

    /**
     * 在Node表新建一个节点的同时添加上与现有节点的联系。
     * 即：创建一个新的子节点，并将其关联到指定的父节点。
     *
     * @param parentNodeId 父节点的ID
     * @param newChildNode 要新建的子节点实体（ID可以为空，保存后会自动生成）
     * @return 是否添加成功
     */
    @Override
    @Transactional
    public boolean addNextNode(Integer parentNodeId, Node newChildNode) {
        // 1. 检查父节点是否存在
        if (baseMapper.selectById(parentNodeId) == null) {
            return false; // 父节点不存在，无法创建子节点并建立关系
        }
        // 2. 保存新的子节点到 Node 表
        // save 方法会自动将新生成的ID设置回 newChildNode 对象
        boolean saveSuccess = super.save(newChildNode);
        if (!saveSuccess || newChildNode.getId() == null) {
            // 保存失败或没有生成ID
            return false;
        }

        // 3. 使用新创建的子节点ID和父节点ID建立关系
        // 直接调用 addNextNodeConnect，因为它已经包含了环状结构检查和重复关系检查
        return addNextNodeConnect(parentNodeId, newChildNode.getId());
    }

    /*
     *删除节点的子节点关系
     */
    @Override
    @Transactional // 涉及到数据库删除操作，确保事务一致性
    public boolean removeNextNode(Integer nodeId, Integer nextnodeId) {
        // 构建查询 Wrapper，用于指定删除条件
        QueryWrapper<NodeNextNode> wrapper = new QueryWrapper<>();
        wrapper.eq("node_id", nodeId);
        wrapper.eq("nextnode_id", nextnodeId);
        // 调用 NodeNextNodeMapper 的 delete 方法，删除指定的关系记录
        int result = nodeNextNodeMapper.delete(wrapper);
        return result > 0; // 如果删除成功（影响行数大于0），则返回 true
    }

    /**
     * 私有方法：检查从 startNodeId 是否能通过关系路径到达 targetNodeId。
     * 用于环状结构检测。
     * 使用广度优先搜索（BFS）
     * @param startNodeId 搜索的起始节点ID
     * @param targetNodeId 目标节点ID
     * @return 如果能从 startNodeId 到达 targetNodeId，则返回 true；否则返回 false。
     */
    private boolean hasPath(Integer startNodeId, Integer targetNodeId) {
        // 如果起始节点或目标节点不存在，或者它们是同一个节点，则直接返回
        if (startNodeId == null || targetNodeId == null) {
            return false;
        }
        if (startNodeId.equals(targetNodeId)) {
            return true; // 从自身可以到达自身
        }
        Queue<Integer> queue = new LinkedList<>();
        Set<Integer> visited = new HashSet<>();
        queue.offer(startNodeId);
        visited.add(startNodeId);
        while (!queue.isEmpty()) {
            Integer currentNode = queue.poll();
            // 获取当前节点的所有直接子节点ID
            QueryWrapper<NodeNextNode> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("node_id", currentNode);
            // selectObjs 返回的是 List<Object>，需要转换为 Integer
            List<Integer> directChildrenIds = nodeNextNodeMapper.selectObjs(queryWrapper.select("nextnode_id"))
                    .stream()
                    .map(obj -> (Integer) obj)
                    .collect(Collectors.toList());
            for (Integer childId : directChildrenIds) {
                if (childId.equals(targetNodeId)) {
                    return true; // 找到了目标节点，存在路径
                }
                if (!visited.contains(childId)) {
                    visited.add(childId);
                    queue.offer(childId);
                }
            }
        }
        return false;
    }
}
